/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2013.12.03                                          *
 *                                                                       *
 *************************************************************************/

#ifndef FreeMAPs_include_COMMON_tbox_InputDatabase_h
#define FreeMAPs_include_COMMON_tbox_InputDatabase_h

#include "FreeMAPsConfig.h"

//COMMON headers
#include "COMMON/base/Common.h"
#include "COMMON/enum/DataType.h"
#include "COMMON/tbox/StringToEnum.h"
#include "COMMON/tbox/Utilities.h"
#include "COMMON/tbox/MapDatabase.h"
#include "COMMON/tbox/Database.h"
#include "COMMON/tbox/DatabaseBox.h"

namespace FreeMAPs
{

/**
 * @brief InputDatabase with nested name.
 *
 * when using SAMRAI::tbox::InputDatabase, it has only the database's name
 * itself, no nested name information. so when output error message, it is
 * not very convienent. this is juse the database with nested name information.
 */
class InputDatabase
{
public:

    explicit
    InputDatabase(
        const boost::shared_ptr<Database> db);

    virtual
    ~InputDatabase();

public:

    /**
     * @return the nested name that contains the parent db information.
     */
    inline const std::string&
    get_nested_name() const;

    inline std::string
    get_name() const;

    inline const boost::shared_ptr<Database>&
    get_database() const;

    inline InputDatabase*
    get_parent_database();

    /**
     * @return the key's line number
     */
    inline int
    get_line_number (
        const std::string& key) const;

    /**
     * @return the root of this database's line number.
     * this will use the parent db's information
     */
    inline int
    get_line_number () const;

public:

    /**
     * set the key's line number in input file.
     */
    void
    set_key_line_number (
        const std::string& key,
        const int line_number);

    /**
     * Return true if the specified key exists in the database and false
     * otherwise.
     */
    bool
    key_exists(
        const std::string& key) const;

    /**
     * Return all keys in the database.
     */
    std::vector<std::string>
    get_keys() const;

    /**
     * Return the size of the array associated with the key.  If the key
     * does not exist, then zero is returned.
     */
    int
    get_array_size(
        const std::string& key) const;

    /**
     * @return true if key = "FREEMAPS_DEFAULT_VALUE" is set.
     */
    bool
    use_default_value (
        const std::string& key) const;

    /**
     * @return a key's data_type
     */
    DataType
    get_key_type (
        const std::string& key) const;

    /**
     * insert the allowable key to the database
     * note that this should be done before calling check_allowable_keys()
     */
    void
    insert_allowable_key (
        const std::string& allowable_key) const;

    /**
     * insert the allowable key to the database
     * note that this should be done before calling check_allowable_keys()
     */
    void
    insert_allowable_keys (
        const std::set<std::string>& allowable_keys) const;

    /**
     * insert the allowable key to the database,
     * and the key's value will be a string vector, each string in the vector
     * will be taken as allowable keys.
     * note that this should be done before calling check_allowable_keys()
     */
    void
    insert_allowable_key_with_items (
        const std::string& allowable_key) const;

    /**
     * some times only one of the keys in para_group1 or para_group2
     * can exists in the db, if both of them are there, show error information.
     */
    void
    check_incompatibility_keys (
        const std::set<std::string>& key_group1,
        const std::set<std::string>& key_group2) const;

    void
    check_incompatibility_keys (
        const std::string& key_1,
        const std::string& key_2) const;

    /**
     * only one of the two keys can exist, and at least one exist.
     * they can not miss neither.
     */
    void
    check_either_or_keys (
        const std::string& key_1,
        const std::string& key_2) const;

    /**
     * check if there is a dittograph in array
     * which is get from db's key.
     * and this is forbiddened.
     */
    void
    check_duplicated_string_value (
        const std::string& key) const;

    /**
     * check if there is a dittograph in array
     * which is get from db's key.
     * and this is forbiddened.
     */
    void
    check_duplicated_integer_value (
        const std::string& key) const;

    /**
     * check all the keys in a database is allowed.
     */
    void
    check_allowable_keys () const;

    void
    check_allowable_key (
        const std::string& key) const;

    /**
     * in some case, a key is in allowable_keys, but for some reason,
     * it is forbiddened.
     */
    void
    check_disallowable_key (
        const std::string& disallowable_key,
        const std::string& reason) const;

    /**
     * in some case, a key is in allowable_keys, but for some reason,
     * it is forbiddened.
     */
    void
    check_disallowable_keys (
        const std::set<std::string>& disallowable_keys,
        const std::string& reason) const;

    /**
     * check if the key's type is the given one
     */
    void
    check_optional_key (
        const std::string& key,
        const DataType data_type,
        const bool can_use_default_value,
        const bool is_array) const;

    /**
     * check if the indispensable key is set in database
     */
    void
    check_requisite_key (
        const std::string& key,
        const DataType data_type,
        const bool can_use_default_value,
        const bool is_array) const;

    /**
     * check if the key is a database,
     * if not, error will be reported.
     * if missing, build a empty database with key.
     */
    void
    check_requisite_database (
        const std::string& key) const;

    /**
     * check if there is a space string in StringVector
     * which is get from db's key
     */
    void
    check_blank_string_key (
        const std::string& key) const;

    /**
     * when a key's value is a string type, it may take value from
     * allowable_values, if not, error will be reported.
     */
    void
    check_allowable_string_value (
        const std::string& key,
        const std::string& tip_info,
        const std::set<std::string>& allowable_values) const;

    void
    check_disallowable_string_value (
        const std::string& key,
        const std::string& disallowable_value,
        const std::string& tip_info) const;

    /**
     * when a key's value is a enum item, check it is rational.
     * if not, error will be reported.
     */
    template<typename T>
    void
    check_allowable_enum_value (
        const std::string& key) const;

    /**
     * when a key's value is a integer type, it may take value
     * from min to max
     */
    void
    check_allowable_integer_value (
        const std::string& key,
        const int min,
        const int max) const;

    void
    check_allowable_min_integer_value (
        const std::string& key,
        const int min) const;

    void
    check_allowable_max_integer_value (
        const std::string& key,
        const int max) const;

    /**
     * when a key's value is a string type, it may take value
     * from min to max
     */
    void
    check_allowable_double_value (
        const std::string& key,
        const double min,
        const double max) const;

    void
    check_allowable_min_double_value (
        const std::string& key,
        const double min) const;

    void
    check_allowable_max_double_value (
        const std::string& key,
        const double max) const;

    /**
     * if an array's size should be dim while input file does not,
     * error will be reported.
     */
    void
    check_vec_size_of_dim(
        const std::string& key) const;

    void
    check_vec_size(
        const std::string& key,
        const int allowable_size) const;

    /**
     * key_1 and key_2 should have same size of array if set.
     */
    void
    check_same_size (
        const std::string& key_1,
        const std::string& key_2) const;

    /**
     * Return whether the specified key represents a database entry.  If
     * the key does not exist, then false is returned.
     */
    bool
    is_database(
        const std::string& key) const;

    /**
     * Create a new database with the specified key name.  If the key already
     * exists in the database, then the old key record is deleted and the new
     * one is silently created in its place.
     */
    InputDatabase*
    put_database(
        const std::string& key) const;

    /**
     * Get the database with the specified key name.  If the specified
     * key does not exist in the database or it is not a database, then
     * an error message is printed and the program exits.
     */
    const InputDatabase*
    get_database (
        const std::string& key) const;

    /**
     * Return whether the specified key represents a boolean entry.  If
     * the key does not exist, then false is returned.
     */
    bool
    is_bool(
        const std::string& key) const;

    /**
     * Create a boolean scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_bool(
        const std::string& key,
        const bool& data) const;

    /**
     * Create a boolean vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     * note that vector<bool> has a not of problem. so use vector<int> instead.
     */
    void
    put_bool_vector(
        const std::string& key,
        const std::vector<int>& data) const;

    /**
     * Create a boolean array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_bool_array(
        const std::string& key,
        const bool* const data,
        const int nelements) const;

    /**
    * Get a boolean entry in the database with the specified key name.
    * If the specified key does not exist in the database or is not a
    * boolean scalar, then an error message is printed and the program
    * exits.
    */
    bool
    get_bool(
        const std::string& key) const;

    /**
     * Get a boolean entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not a boolean scalar,
     * then an error message is printed and the program exits.
     */
    bool
    get_bool_with_default(
        const std::string& key,
        const bool& defaultvalue) const;

    /**
     * Get a boolean entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a boolean vector, then an error message is printed and
     * the program exits.
     */
    std::vector<bool>
    get_bool_vector(
        const std::string& key) const;

    /**
     * Get a boolean entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a boolean array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     */
    void
    get_bool_array(
        const std::string& key,
        bool* data,
        const int nelements) const;

    void
    get_bool_vector_with_default (
        const std::string& key,
        const bool default_value,
        const int default_size,
        std::vector<bool>& result_array) const;

    /**
     * Return whether the specified key represents a box entry.  If
     * the key does not exist, then false is returned.
     */
    bool
    is_database_box(
        const std::string& key) const;

    /**
     * Create a box scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_database_box(
        const std::string& key,
        const DatabaseBox& data) const;

    /**
     * Create a box vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_database_box_vector(
        const std::string& key,
        const std::vector<DatabaseBox>& data) const;

    /**
     * Create a box array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_database_box_array(
        const std::string& key,
        const DatabaseBox* const data,
        const int nelements) const;

    /**
     * Get a box entry in the database with the specified key name.
     * If the specified key does not exist in the database or is not a
     * box scalar, then an error message is printed and the program
     * exits.
     */
    DatabaseBox
    get_database_box(
        const std::string& key) const;

    /**
     * Get a box entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not a box scalar,
     * then an error message is printed and the program exits.
     */
    DatabaseBox
    get_database_box_with_default(
        const std::string& key,
        const DatabaseBox& defaultvalue) const;

    /**
     * Get a box entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a box vector, then an error message is printed and
     * the program exits.
     *
     * @param key Key name in database.
     */
    std::vector<DatabaseBox>
    get_database_box_vector(
        const std::string& key) const;

    /**
     * Get a box entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a box array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     */
    void
    get_database_box_array(
        const std::string& key,
        DatabaseBox* data,
        const int nelements) const;

    /**
     * Return whether the specified key represents a character entry.  If
     * the key does not exist, then false is returned.
     */
    bool
    is_char(
        const std::string& key) const;

    /**
     * Create a character scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_char(
        const std::string& key,
        const char& data) const;

    /**
     * Create a character vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_char_vector(
        const std::string& key,
        const std::vector<char>& data) const;

    /**
     * Create a character array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_char_array(
        const std::string& key,
        const char* const data,
        const int nelements) const;

    /**
     * Get a character entry in the database with the specified key name.
     * If the specified key does not exist in the database or is not an
     * character scalar, then an error message is printed and the program
     * exits.
     */
    char
    get_char(
        const std::string& key) const;

    /**
     * Get a character entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not a character scalar,
     * then an error message is printed and the program exits.
     */
    char
    get_char_with_default(
        const std::string& key,
        const char& defaultvalue) const;

    /**
     * Get a character entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a character vector, then an error message is printed and
     * the program exits.
     */
    std::vector<char>
    get_char_vector(
        const std::string& key) const;

    /**
     * Get a character entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a character array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     */
    void
    get_char_array(
        const std::string& key,
        char* data,
        const int nelements) const;

    void
    get_char_vector_with_default (
        const std::string& key,
        const char default_value,
        const int default_size,
        std::vector<char>& result_array) const;

    /**
     * Return whether the specified key represents a complex entry.  If
     * the key does not exist, then false is returned.  Complex values
     * may be promoted from integers, floats, or doubles.
     */
    bool
    is_complex(
        const std::string& key) const;

    /**
     * Create a complex scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_complex(
        const std::string& key,
        const Complex& data) const;

    /**
     * Create a complex vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_complex_vector(
        const std::string& key,
        const std::vector<Complex>& data) const;

    /**
     * Create a complex array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_complex_array(
        const std::string& key,
        const Complex* const data,
        const int nelements) const;

    /**
     * Get a complex entry in the database with the specified key name.
     * If the specified key does not exist in the database or is not a
     * complex scalar, then an error message is printed and the program
     * exits.  Complex values may be promoted from integers, floats, or
     * doubles.
     */
    Complex
    get_complex(
        const std::string& key) const;

    /**
     * Get a complex entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not a complex scalar,
     * then an error message is printed and the program exits.  Complex
     * values may be promoted from integers, floats, or doubles.
     */
    Complex
    get_complex_with_default(
        const std::string& key,
        const Complex& defaultvalue) const;

    /**
     * Get a complex entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a complex vector, then an error message is printed and
     * the program exits.  Complex values may be promoted from integers,
     * floats, or doubles.
     */
    std::vector<Complex>
    get_complex_vector(
        const std::string& key) const;

    /**
     * Get a complex entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a complex array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     * Complex values may be promoted from integers, floats, or doubles.
     */
    void
    get_complex_array(
        const std::string& key,
        Complex* data,
        const int nelements) const;

    /**
     * Return whether the specified key represents a string-string map entry.  If
     * the key does not exist, then false is returned.
     */
    bool
    is_string_string_map(
        const std::string& key) const;

    /**
     * Create a complex vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_string_string_map(
        const std::string& key,
        const std::map<std::string, std::string>& data) const;

    /**
     * Get a complex entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a complex vector, then an error message is printed and
     * the program exits.  Complex values may be promoted from integers,
     * floats, or doubles.
     */
    std::map<std::string, std::string>
    get_string_string_map(
        const std::string& key) const;

    std::pair<std::string, std::string>
    get_string_string_pair(
        const std::string& key) const;

    /**
     * Return whether the specified key represents a double entry.  If
     * the key does not exist, then false is returned.  Double values
     * may be promoted from integers or floats.
     */
    bool
    is_double(
        const std::string& key) const;

    /**
     * Create a double scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_double(
        const std::string& key,
        const double& data) const;

    /**
     * Create a double vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_double_vector(
        const std::string& key,
        const std::vector<double>& data) const;

    /**
     * Create a double array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_double_array(
        const std::string& key,
        const double* const data,
        const int nelements) const;

    /**
     * Get a double entry in the database with the specified key name.
     * If the specified key does not exist in the database or is not a
     * double scalar, then an error message is printed and the program
     * exits.  Double values may be promoted from integers or floats.
     */
    double
    get_double(
        const std::string& key) const;

    /**
     * Get a double entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not a double scalar, then
     * an error message is printed and the program exits.  Double values may
     * be promoted from integers or floats.
     */
    double
    get_double_with_default(
        const std::string& key,
        const double& defaultvalue) const;

    /**
     * Get a double entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a double vector, then an error message is printed and
     * the program exits.  Double values may be promoted from integers
     * or floats.
     */
    std::vector<double>
    get_double_vector(
        const std::string& key) const;

    /**
     * Get a double entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a double array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     * Double values may be promoted from integers or floats.
     */
    void
    get_double_array(
        const std::string& key,
        double* data,
        const int nelements) const;

    void
    get_double_vector_with_default (
        const std::string& key,
        const double default_value,
        const int default_size,
        std::vector<double>& result_array) const;

    /**
     * Return whether the specified key represents a float entry.  If
     * the key does not exist, then false is returned.  Float values
     * may be promoted from integers or silently truncated from doubles.
     */
    bool
    is_float(
        const std::string& key) const;

    /**
     * Create a float scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_float(
        const std::string& key,
        const float& data) const;

    /**
     * Create a float vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_float_vector(
        const std::string& key,
        const std::vector<float>& data) const;

    /**
     * Create a float array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_float_array(
        const std::string& key,
        const float* const data,
        const int nelements) const;

    /**
     * Get a float entry in the database with the specified key name.
     * If the specified key does not exist in the database or is not a
     * float scalar, then an error message is printed and the program
     * exits.  Float values may be promoted from integers or silently
     * truncated from doubles.
     */
    float
    get_float(
        const std::string& key) const;

    /**
     * Get a float entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not a float scalar, then
     * an error message is printed and the program exits.  Float values may
     * be promoted from integers or silently truncated from doubles.
     */
    float
    get_float_with_default(
        const std::string& key,
        const float& defaultvalue) const;

    /**
     * Get a float entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a float vector, then an error message is printed and
     * the program exits.  Float values may be promoted from integers
     * or silently truncated from doubles.
     */
    std::vector<float>
    get_float_vector(
        const std::string& key) const;

    /**
     * Get a float entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a float array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     * Float values may be promoted from integers or silently truncated
     * from doubles.
     */
    void
    get_float_array(
        const std::string& key,
        float* data,
        const int nelements) const;

    /**
     * Return whether the specified key represents an integer entry.  If
     * the key does not exist, then false is returned.
     */
    bool
    is_integer(
        const std::string& key) const;

    /**
     * Create an integer scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_integer(
        const std::string& key,
        const int& data) const;

    /**
     * Create an integer vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_integer_vector(
        const std::string& key,
        const std::vector<int>& data) const;

    /**
     * Create an integer array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_integer_array(
        const std::string& key,
        const int* const data,
        const int nelements) const;

    /**
     * Get an integer entry in the database with the specified key name.
     * If the specified key does not exist in the database or is not an
     * integer scalar, then an error message is printed and the program
     * exits.
     */
    int
    get_integer(
        const std::string& key) const;

    /**
     * Get an integer entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not an integer scalar,
     * then an error message is printed and the program exits.
     */
    int
    get_integer_with_default(
        const std::string& key,
        const int& defaultvalue) const;

    /**
     * Get an integer entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not an integer vector, then an error message is printed and
     * the program exits.
     */
    std::vector<int>
    get_integer_vector(
        const std::string& key) const;

    /**
     * Get an integer entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not an integer array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     */
    void
    get_integer_array(
        const std::string& key,
        int* data,
        const int nelements) const;

    void
    get_integer_vector_with_default (
        const std::string& key,
        const int default_value,
        const int default_size,
        std::vector<int>& result_array) const;

    /**
     * Return whether the specified key represents a std::string entry.  If
     * the key does not exist, then false is returned.
     */
    bool
    is_string(
        const std::string& key) const;

    /**
     * Create a string scalar entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_string(
        const std::string& key,
        const std::string& data) const;

    /**
     * Create a string vector entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_string_vector(
        const std::string& key,
        const std::vector<std::string>& data) const;

    /**
     * Create a string array entry in the database with the specified
     * key name.  If the key already exists in the database, then the old
     * key record is deleted and the new one is silently created in its place.
     */
    void
    put_string_array(
        const std::string& key,
        const std::string* const data,
        const int nelements) const;

    /**
     * Get a string entry in the database with the specified key name.
     * If the specified key does not exist in the database or is not an
     * string scalar, then an error message is printed and the program
     * exits.
     */
    std::string
    get_string(
        const std::string& key) const;

    /**
     * Get a string entry in the database with the specified key name.
     * If the specified key does not exist in the database, then the default
     * value is returned.  If the key exists but is not a string scalar,
     * then an error message is printed and the program exits.
     */
    std::string
    get_string_with_default(
        const std::string& key,
        const std::string& defaultvalue) const;

    /**
     * Get a string entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a string vector, then an error message is printed and
     * the program exits.
     */
    std::vector<std::string>
    get_string_vector(
        const std::string& key) const;

    /**
     * Get a string entry from the database with the specified key
     * name.  If the specified key does not exist in the database or
     * is not a string array, then an error message is printed and
     * the program exits.  The specified number of elements must match
     * exactly the number of elements in the array in the database.
     */
    void
    get_string_array(
        const std::string& key,
        std::string* data,
        const int nelements) const;

    /**
     * return a enum item build from string,
     * the enum must be defined in COMMON/enum/
     * so that function of enum_to_string or string_to_enum can be called.
     * if it is not a correct enum item, error will be reported
     * and exits.
     * make sure the key exists and the return value can NOT be INVALID_VALUE.
     */
    template<typename T>
    T
    get_enum (
        const std::string& key) const;

    /**
     * return a enum item build from string,
     * the enum must be defined in COMMON/enum/
     * so that function of enum_to_string or string_to_enum can be called.
     * if it is not a correct enum item, error will be reported
     * and exits.
     */
    template<typename T>
    T
    get_enum_with_default (
        const std::string& key,
        const T default_value) const;

    /**
     * some times, not all enum's item is allowable choice.
     * only those in allowable_items can be taken, get the value with check.
     */
    template<typename T>
    T
    get_enum_with_default (
        const std::string& key,
        const T default_value,
        const std::set<std::string>& allowable_items) const;

    /**
     * an object is often build it's information from a dababase,
     * but somtime the object is a virtual class, it's type should be read from the database.
     * that means before read from the database, it's type is not known.
     * this is often the case, so we provide this function to dealing this case.
     */
    template<typename T>
    T
    get_enum_from_subdatabase(
        const std::string& key,
        const std::string& key_in_subdb) const;

    template<typename T>
    T
    get_enum_from_subdatabase(
        const std::string& key,
        const std::string& key_in_subdb,
        const T default_value) const;

    /**
     * return a vector consisted by enum item.
     * if any item in the vector is not correct,
     * error will be reported and exits.
     * the key must exists.
     */
    template<typename T>
    std::vector<T>
    get_enum_vector (
        const std::string& key) const;

    /**
     * return a string vector with default choice.
     */
    void
    get_string_vector_with_default (
        const std::string& key,
        const std::string& default_value,
        const int default_size,
        std::vector<std::string>& result_array) const;

    friend std::ostream&
    operator << (
        std::ostream& os,
        const InputDatabase& rhs);

    std::string
    get_file_name ()const;

    const InputDatabase* get_top_database()const;
private:

    std::string d_nested_name;

    /*
     * all get() functions in Database are not const,
     * so set it to mutable to avoid const_cast.
     */
    mutable boost::shared_ptr<Database> d_database;

    InputDatabase* d_parent_db;
    bool d_is_top_db;

    /*
     * map<key, sub_database>
     */
    std::map<std::string, InputDatabase*> d_sub_databases;

    /*
     * save the key's line number in input file, so that error will be reported
     * more accurately.
     * map <key, line_number>
     */
    std::map <std::string, int> d_key_line_number;

    /*
     * used to check whether all the keys in the database is allowable.
     * the allowable keys will be supplied by the user while the keys of the
     * database is build from input file.
     */
    mutable std::set<std::string> d_allowable_keys;

    mutable MapDatabase d_map_database;

private:

    DISALLOW_COPY_AND_ASSIGN (InputDatabase);
};

}

#include "COMMON/tbox/InputDatabase.inl"

#endif
